Skip to content

Zoom in monitor area under a mouse#39

Open
PlkMarudny wants to merge 6 commits into
ronak-create:mainfrom
PlkMarudny:monitor-zoom
Open

Zoom in monitor area under a mouse#39
PlkMarudny wants to merge 6 commits into
ronak-create:mainfrom
PlkMarudny:monitor-zoom

Conversation

@PlkMarudny

@PlkMarudny PlkMarudny commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Sometimes a closer look is profitable. When hovering a mouse over a monitor, mousewheel action allows to zoom in the area under the cursor. There is also button to reset zoom to 100%.

Type of change

  • Bug fix
  • New feature (UI)
  • Docs
  • Refactor / internal

How was it verified?

  • node --check server.js && node --check app.js && node --check mcp-server.js passes
  • Opened the editor and confirmed the change in preview
  • Confirmed the change in an export (fast or realtime), if it affects rendering
  • Updated CLAUDE.md / README.md if the schema, props, or API changed

Checklist

  • No new runtime dependencies added
  • Preview and export render identically (single compositor)
  • Commits are focused and messages are descriptive

Summary by CodeRabbit

  • New Features
    • Added cursor-centered mouse-wheel zoom for the Program Monitor (with an upper zoom cap).
    • Added a “100%” control to reset to the fit-to-stage baseline.
    • Enabled panning while zoomed via middle-mouse or Alt+drag, with grab/grabbing cursor feedback.
  • Bug Fixes
    • Improved safe-area overlay positioning so it remains locked to the preview during zoom, pan, scroll, and resize.
  • Documentation
    • Updated README with the new Program Monitor zoom and pan behavior.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c03acca2-da2e-4e86-a7fb-69c9e89b8a74

📥 Commits

Reviewing files that changed from the base of the PR and between 049484f and fbcd69a.

📒 Files selected for processing (4)
  • README.md
  • app.js
  • index.html
  • style.css
🚧 Files skipped from review as they are similar to previous changes (4)
  • README.md
  • index.html
  • style.css
  • app.js

📝 Walkthrough

Walkthrough

The program monitor now supports cursor-centered zooming, a Fit reset control, and panning with middle mouse or Alt-drag. Zoom state, native scrolling, safe-overlay positioning, monitor layout, and related documentation are updated.

Changes

Program Monitor View Controls

Layer / File(s) Summary
Monitor structure and view state
app.js, index.html, style.css
Monitor zoom state, Fit control markup, nested scrolling containers, title styling, and base layout establish the view-control structure.
Zoom, pan, and overlay behavior
app.js, style.css
Wheel zoom, Fit reset, native-scroll panning, pointer gesture exclusions, and safe-overlay refresh behavior are implemented.
Monitor presentation and documentation
style.css, README.md
Zoom cursor and preview sizing rules, VU meter spacing adjustments, and the Program Monitor zoom feature description are updated.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant monitorScroll
  participant state
  participant preview
  participant safeOverlay
  User->>monitorScroll: Wheel over preview
  monitorScroll->>state: Update viewZoom
  state->>preview: Apply rendered size and scroll offsets
  preview->>safeOverlay: Refresh overlay position
  User->>monitorScroll: Middle mouse or Alt-drag
  monitorScroll->>preview: Update native scroll offsets
  preview->>safeOverlay: Refresh overlay position
  User->>monitorScroll: Click Fit
  monitorScroll->>state: Reset viewZoom to 1
  state->>preview: Restore fit size
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clearly related to the main change: zooming the monitor area under the mouse cursor.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
index.html (1)

65-69: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

"Reset to 100%" label/tooltip mismatches actual "fit stage" behavior.

state.viewZoom = 1 is documented in app.js as "program-monitor display zoom (1 = fit stage)", i.e., it's a fit-to-stage baseline, not a literal native-pixel 100% zoom. Labeling the button "Reset to 100%" (and tooltip "Reset monitor zoom to fit (100%)") conflates these two concepts and may confuse users who expect true 1:1 pixel scale.

✏️ Suggested wording
-          <button class="btn tiny hidden" id="btnZoom100" title="Reset monitor zoom to fit (100%)">...Reset to 100%</button>
+          <button class="btn tiny hidden" id="btnZoom100" title="Reset monitor zoom to fit">...Reset zoom</button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@index.html` around lines 65 - 69, Update the Program Monitor reset button
identified by btnZoom100 so its visible label and title describe restoring the
fit-to-stage baseline rather than “100%” or native-pixel zoom; keep the existing
reset behavior unchanged.
app.js (2)

4430-4457: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

No bounds on state.viewPan.

Neither the wheel-zoom pan adjustment nor the drag-pan pointermove handler clamps state.viewPan, so a user can drag the zoomed canvas entirely out of the visible stage. Recovery only via the "Reset to 100%" button, which is a usable fallback but still a rough edge.

🔧 Suggested clamp using existing bounding-rect data
 function applyMonitorView() {
   const z = state.viewZoom, pan = state.viewPan;
   const zoomed = z > 1.001;
   if (!zoomed) {
     state.viewZoom = 1;
     state.viewPan = { x: 0, y: 0 };
     els.preview.style.transform = "";
   } else {
+    const stage = els.monitorStage.getBoundingClientRect();
+    const maxX = (stage.width * (z - 1)) / 2;
+    const maxY = (stage.height * (z - 1)) / 2;
+    pan.x = clamp(pan.x, -maxX, maxX);
+    pan.y = clamp(pan.y, -maxY, maxY);
     els.preview.style.transform = `translate(${pan.x}px, ${pan.y}px) scale(${z})`;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.js` around lines 4430 - 4457, Clamp state.viewPan to valid bounds after
both the wheel-zoom adjustment and drag-pan updates so the zoomed canvas cannot
be moved entirely outside the visible stage. Reuse the existing monitor stage
and content bounding-rect data where available, and apply the clamp before
applyMonitorView() in the pointermove path and before storing the zoom-adjusted
pan.

4398-4457: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

updateSafeOverlay() forces layout on every wheel tick and pan pointermove.

applyMonitorView() calls updateSafeOverlay() (two getBoundingClientRect() reads) right after mutating els.preview.style.transform, and this runs on every wheel event and every pointermove while panning (when state.guides is on). That's a write-then-read cycle repeated at high frequency, forcing synchronous layout each time and risking visible jank during zoom/pan.

⚡ Suggested throttling via requestAnimationFrame
+let viewRafPending = false;
+function scheduleApplyMonitorView() {
+  if (viewRafPending) return;
+  viewRafPending = true;
+  requestAnimationFrame(() => { viewRafPending = false; applyMonitorView(); });
+}

Then call scheduleApplyMonitorView() from the wheel and pointermove handlers instead of applyMonitorView() directly (transform mutation itself can stay immediate if desired; the goal is to coalesce the updateSafeOverlay reflow to once per frame).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app.js` around lines 4398 - 4457, Throttle monitor view updates with
requestAnimationFrame: add a scheduling helper around applyMonitorView that
coalesces repeated calls into one frame, and use it from the wheel and
pointermove handlers instead of calling applyMonitorView directly. Preserve
immediate reset behavior for resetMonitorView and ensure updateSafeOverlay runs
at most once per animation frame during zooming and panning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app.js`:
- Around line 4430-4457: Clamp state.viewPan to valid bounds after both the
wheel-zoom adjustment and drag-pan updates so the zoomed canvas cannot be moved
entirely outside the visible stage. Reuse the existing monitor stage and content
bounding-rect data where available, and apply the clamp before
applyMonitorView() in the pointermove path and before storing the zoom-adjusted
pan.
- Around line 4398-4457: Throttle monitor view updates with
requestAnimationFrame: add a scheduling helper around applyMonitorView that
coalesces repeated calls into one frame, and use it from the wheel and
pointermove handlers instead of calling applyMonitorView directly. Preserve
immediate reset behavior for resetMonitorView and ensure updateSafeOverlay runs
at most once per animation frame during zooming and panning.

In `@index.html`:
- Around line 65-69: Update the Program Monitor reset button identified by
btnZoom100 so its visible label and title describe restoring the fit-to-stage
baseline rather than “100%” or native-pixel zoom; keep the existing reset
behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3637d4de-0f6f-4d48-ada1-f85ff1551fba

📥 Commits

Reviewing files that changed from the base of the PR and between d584430 and a12b424.

📒 Files selected for processing (3)
  • app.js
  • index.html
  • style.css

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app.js`:
- Around line 4456-4474: The monitor update paths currently bypass the
coalescing helper. Replace direct applyMonitorView() calls in frequent monitor
zoom/pan update handlers and add scheduleMonitorView() after the scroll
handler’s updateSafeOverlay() call, while preserving resetMonitorView()’s
immediate applyMonitorView() behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d9f153c-4c59-480a-a4a0-c0f6c0d37c9c

📥 Commits

Reviewing files that changed from the base of the PR and between a12b424 and 0d81466.

📒 Files selected for processing (4)
  • README.md
  • app.js
  • index.html
  • style.css

Comment thread app.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant